1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.base.Preconditions.checkState;
21  import static com.google.common.collect.CollectPreconditions.checkRemove;
22  
23  import com.google.common.annotations.GwtCompatible;
24  import com.google.common.base.Objects;
25  
26  import java.io.Serializable;
27  import java.util.Collection;
28  import java.util.Iterator;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import javax.annotation.Nullable;
33  
34  /**
35   * A general-purpose bimap implementation using any two backing {@code Map}
36   * instances.
37   *
38   * <p>Note that this class contains {@code equals()} calls that keep it from
39   * supporting {@code IdentityHashMap} backing maps.
40   *
41   * @author Kevin Bourrillion
42   * @author Mike Bostock
43   */
44  @GwtCompatible(emulated = true)
45  abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
46      implements BiMap<K, V>, Serializable {
47  
48    private transient Map<K, V> delegate;
49    transient AbstractBiMap<V, K> inverse;
50  
51    /** Package-private constructor for creating a map-backed bimap. */
52    AbstractBiMap(Map<K, V> forward, Map<V, K> backward) {
53      setDelegates(forward, backward);
54    }
55  
56    /** Private constructor for inverse bimap. */
57    private AbstractBiMap(Map<K, V> backward, AbstractBiMap<V, K> forward) {
58      delegate = backward;
59      inverse = forward;
60    }
61  
62    @Override protected Map<K, V> delegate() {
63      return delegate;
64    }
65  
66    /**
67     * Returns its input, or throws an exception if this is not a valid key.
68     */
69    K checkKey(@Nullable K key) {
70      return key;
71    }
72  
73    /**
74     * Returns its input, or throws an exception if this is not a valid value.
75     */
76    V checkValue(@Nullable V value) {
77      return value;
78    }
79  
80    /**
81     * Specifies the delegate maps going in each direction. Called by the
82     * constructor and by subclasses during deserialization.
83     */
84    void setDelegates(Map<K, V> forward, Map<V, K> backward) {
85      checkState(delegate == null);
86      checkState(inverse == null);
87      checkArgument(forward.isEmpty());
88      checkArgument(backward.isEmpty());
89      checkArgument(forward != backward);
90      delegate = forward;
91      inverse = new Inverse<V, K>(backward, this);
92    }
93  
94    void setInverse(AbstractBiMap<V, K> inverse) {
95      this.inverse = inverse;
96    }
97  
98    // Query Operations (optimizations)
99  
100   @Override public boolean containsValue(@Nullable Object value) {
101     return inverse.containsKey(value);
102   }
103 
104   // Modification Operations
105 
106   @Override public V put(@Nullable K key, @Nullable V value) {
107     return putInBothMaps(key, value, false);
108   }
109 
110   @Override
111   public V forcePut(@Nullable K key, @Nullable V value) {
112     return putInBothMaps(key, value, true);
113   }
114 
115   private V putInBothMaps(@Nullable K key, @Nullable V value, boolean force) {
116     checkKey(key);
117     checkValue(value);
118     boolean containedKey = containsKey(key);
119     if (containedKey && Objects.equal(value, get(key))) {
120       return value;
121     }
122     if (force) {
123       inverse().remove(value);
124     } else {
125       checkArgument(!containsValue(value), "value already present: %s", value);
126     }
127     V oldValue = delegate.put(key, value);
128     updateInverseMap(key, containedKey, oldValue, value);
129     return oldValue;
130   }
131 
132   private void updateInverseMap(
133       K key, boolean containedKey, V oldValue, V newValue) {
134     if (containedKey) {
135       removeFromInverseMap(oldValue);
136     }
137     inverse.delegate.put(newValue, key);
138   }
139 
140   @Override public V remove(@Nullable Object key) {
141     return containsKey(key) ? removeFromBothMaps(key) : null;
142   }
143 
144   private V removeFromBothMaps(Object key) {
145     V oldValue = delegate.remove(key);
146     removeFromInverseMap(oldValue);
147     return oldValue;
148   }
149 
150   private void removeFromInverseMap(V oldValue) {
151     inverse.delegate.remove(oldValue);
152   }
153 
154   // Bulk Operations
155 
156   @Override public void putAll(Map<? extends K, ? extends V> map) {
157     for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
158       put(entry.getKey(), entry.getValue());
159     }
160   }
161 
162   @Override public void clear() {
163     delegate.clear();
164     inverse.delegate.clear();
165   }
166 
167   // Views
168 
169   @Override
170   public BiMap<V, K> inverse() {
171     return inverse;
172   }
173 
174   private transient Set<K> keySet;
175 
176   @Override public Set<K> keySet() {
177     Set<K> result = keySet;
178     return (result == null) ? keySet = new KeySet() : result;
179   }
180 
181   private class KeySet extends ForwardingSet<K> {
182     @Override protected Set<K> delegate() {
183       return delegate.keySet();
184     }
185 
186     @Override public void clear() {
187       AbstractBiMap.this.clear();
188     }
189 
190     @Override public boolean remove(Object key) {
191       if (!contains(key)) {
192         return false;
193       }
194       removeFromBothMaps(key);
195       return true;
196     }
197 
198     @Override public boolean removeAll(Collection<?> keysToRemove) {
199       return standardRemoveAll(keysToRemove);
200     }
201 
202     @Override public boolean retainAll(Collection<?> keysToRetain) {
203       return standardRetainAll(keysToRetain);
204     }
205 
206     @Override public Iterator<K> iterator() {
207       return Maps.keyIterator(entrySet().iterator());
208     }
209   }
210 
211   private transient Set<V> valueSet;
212 
213   @Override public Set<V> values() {
214     /*
215      * We can almost reuse the inverse's keySet, except we have to fix the
216      * iteration order so that it is consistent with the forward map.
217      */
218     Set<V> result = valueSet;
219     return (result == null) ? valueSet = new ValueSet() : result;
220   }
221 
222   private class ValueSet extends ForwardingSet<V> {
223     final Set<V> valuesDelegate = inverse.keySet();
224 
225     @Override protected Set<V> delegate() {
226       return valuesDelegate;
227     }
228 
229     @Override public Iterator<V> iterator() {
230       return Maps.valueIterator(entrySet().iterator());
231     }
232 
233     @Override public Object[] toArray() {
234       return standardToArray();
235     }
236 
237     @Override public <T> T[] toArray(T[] array) {
238       return standardToArray(array);
239     }
240 
241     @Override public String toString() {
242       return standardToString();
243     }
244   }
245 
246   private transient Set<Entry<K, V>> entrySet;
247 
248   @Override public Set<Entry<K, V>> entrySet() {
249     Set<Entry<K, V>> result = entrySet;
250     return (result == null) ? entrySet = new EntrySet() : result;
251   }
252 
253   private class EntrySet extends ForwardingSet<Entry<K, V>> {
254     final Set<Entry<K, V>> esDelegate = delegate.entrySet();
255 
256     @Override protected Set<Entry<K, V>> delegate() {
257       return esDelegate;
258     }
259 
260     @Override public void clear() {
261       AbstractBiMap.this.clear();
262     }
263 
264     @Override public boolean remove(Object object) {
265       if (!esDelegate.contains(object)) {
266         return false;
267       }
268 
269       // safe because esDelgate.contains(object).
270       Entry<?, ?> entry = (Entry<?, ?>) object;
271       inverse.delegate.remove(entry.getValue());
272       /*
273        * Remove the mapping in inverse before removing from esDelegate because
274        * if entry is part of esDelegate, entry might be invalidated after the
275        * mapping is removed from esDelegate.
276        */
277       esDelegate.remove(entry);
278       return true;
279     }
280 
281     @Override public Iterator<Entry<K, V>> iterator() {
282       final Iterator<Entry<K, V>> iterator = esDelegate.iterator();
283       return new Iterator<Entry<K, V>>() {
284         Entry<K, V> entry;
285 
286         @Override public boolean hasNext() {
287           return iterator.hasNext();
288         }
289 
290         @Override public Entry<K, V> next() {
291           entry = iterator.next();
292           final Entry<K, V> finalEntry = entry;
293 
294           return new ForwardingMapEntry<K, V>() {
295             @Override protected Entry<K, V> delegate() {
296               return finalEntry;
297             }
298 
299             @Override public V setValue(V value) {
300               // Preconditions keep the map and inverse consistent.
301               checkState(contains(this), "entry no longer in map");
302               // similar to putInBothMaps, but set via entry
303               if (Objects.equal(value, getValue())) {
304                 return value;
305               }
306               checkArgument(!containsValue(value),
307                   "value already present: %s", value);
308               V oldValue = finalEntry.setValue(value);
309               checkState(Objects.equal(value, get(getKey())),
310                   "entry no longer in map");
311               updateInverseMap(getKey(), true, oldValue, value);
312               return oldValue;
313             }
314           };
315         }
316 
317         @Override public void remove() {
318           checkRemove(entry != null);
319           V value = entry.getValue();
320           iterator.remove();
321           removeFromInverseMap(value);
322         }
323       };
324     }
325 
326     // See java.util.Collections.CheckedEntrySet for details on attacks.
327 
328     @Override public Object[] toArray() {
329       return standardToArray();
330     }
331     @Override public <T> T[] toArray(T[] array) {
332       return standardToArray(array);
333     }
334     @Override public boolean contains(Object o) {
335       return Maps.containsEntryImpl(delegate(), o);
336     }
337     @Override public boolean containsAll(Collection<?> c) {
338       return standardContainsAll(c);
339     }
340     @Override public boolean removeAll(Collection<?> c) {
341       return standardRemoveAll(c);
342     }
343     @Override public boolean retainAll(Collection<?> c) {
344       return standardRetainAll(c);
345     }
346   }
347 
348   /** The inverse of any other {@code AbstractBiMap} subclass. */
349   private static class Inverse<K, V> extends AbstractBiMap<K, V> {
350     private Inverse(Map<K, V> backward, AbstractBiMap<V, K> forward) {
351       super(backward, forward);
352     }
353 
354     /*
355      * Serialization stores the forward bimap, the inverse of this inverse.
356      * Deserialization calls inverse() on the forward bimap and returns that
357      * inverse.
358      *
359      * If a bimap and its inverse are serialized together, the deserialized
360      * instances have inverse() methods that return the other.
361      */
362 
363     @Override
364     K checkKey(K key) {
365       return inverse.checkValue(key);
366     }
367 
368     @Override
369     V checkValue(V value) {
370       return inverse.checkKey(value);
371     }
372   }
373 }
374